home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / dns / message.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  31.6 KB  |  1,026 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. '''DNS Messages'''
  5. import cStringIO
  6. import random
  7. import struct
  8. import sys
  9. import time
  10. import dns.exception as dns
  11. import dns.flags as dns
  12. import dns.name as dns
  13. import dns.opcode as dns
  14. import dns.rcode as dns
  15. import dns.rdata as dns
  16. import dns.rdataclass as dns
  17. import dns.rdatatype as dns
  18. import dns.rrset as dns
  19. import dns.renderer as dns
  20. import dns.tsig as dns
  21.  
  22. class ShortHeader(dns.exception.FormError):
  23.     '''Raised if the DNS packet passed to from_wire() is too short.'''
  24.     pass
  25.  
  26.  
  27. class TrailingJunk(dns.exception.FormError):
  28.     '''Raised if the DNS packet passed to from_wire() has extra junk
  29.     at the end of it.'''
  30.     pass
  31.  
  32.  
  33. class UnknownHeaderField(dns.exception.DNSException):
  34.     '''Raised if a header field name is not recognized when converting from
  35.     text into a message.'''
  36.     pass
  37.  
  38.  
  39. class BadEDNS(dns.exception.FormError):
  40.     '''Raised if an OPT record occurs somewhere other than the start of
  41.     the additional data section.'''
  42.     pass
  43.  
  44.  
  45. class BadTSIG(dns.exception.FormError):
  46.     '''Raised if a TSIG record occurs somewhere other than the end of
  47.     the additional data section.'''
  48.     pass
  49.  
  50.  
  51. class UnknownTSIGKey(dns.exception.DNSException):
  52.     """Raised if we got a TSIG but don't know the key."""
  53.     pass
  54.  
  55.  
  56. class Message(object):
  57.     """A DNS message.
  58.  
  59.     @ivar id: The query id; the default is a randomly chosen id.
  60.     @type id: int
  61.     @ivar flags: The DNS flags of the message.  @see: RFC 1035 for an
  62.     explanation of these flags.
  63.     @type flags: int
  64.     @ivar question: The question section.
  65.     @type question: list of dns.rrset.RRset objects
  66.     @ivar answer: The answer section.
  67.     @type answer: list of dns.rrset.RRset objects
  68.     @ivar authority: The authority section.
  69.     @type authority: list of dns.rrset.RRset objects
  70.     @ivar additional: The additional data section.
  71.     @type additional: list of dns.rrset.RRset objects
  72.     @ivar edns: The EDNS level to use.  The default is -1, no Edns.
  73.     @type edns: int
  74.     @ivar ednsflags: The EDNS flags
  75.     @type ednsflags: long
  76.     @ivar payload: The EDNS payload size.  The default is 0.
  77.     @type payload: int
  78.     @ivar request_payload: The associated request's EDNS payload size.
  79.     @type request_payload: int
  80.     @ivar keyring: The TSIG keyring to use.  The default is None.
  81.     @type keyring: dict
  82.     @ivar keyname: The TSIG keyname to use.  The default is None.
  83.     @type keyname: dns.name.Name object
  84.     @ivar request_mac: The TSIG MAC of the request message associated with
  85.     this message; used when validating TSIG signatures.   @see: RFC 2845 for
  86.     more information on TSIG fields.
  87.     @type request_mac: string
  88.     @ivar fudge: TSIG time fudge; default is 300 seconds.
  89.     @type fudge: int
  90.     @ivar original_id: TSIG original id; defaults to the message's id
  91.     @type original_id: int
  92.     @ivar tsig_error: TSIG error code; default is 0.
  93.     @type tsig_error: int
  94.     @ivar other_data: TSIG other data.
  95.     @type other_data: string
  96.     @ivar mac: The TSIG MAC for this message.
  97.     @type mac: string
  98.     @ivar xfr: Is the message being used to contain the results of a DNS
  99.     zone transfer?  The default is False.
  100.     @type xfr: bool
  101.     @ivar origin: The origin of the zone in messages which are used for
  102.     zone transfers or for DNS dynamic updates.  The default is None.
  103.     @type origin: dns.name.Name object
  104.     @ivar tsig_ctx: The TSIG signature context associated with this
  105.     message.  The default is None.
  106.     @type tsig_ctx: hmac.HMAC object
  107.     @ivar had_tsig: Did the message decoded from wire format have a TSIG
  108.     signature?
  109.     @type had_tsig: bool
  110.     @ivar multi: Is this message part of a multi-message sequence?  The
  111.     default is false.  This variable is used when validating TSIG signatures
  112.     on messages which are part of a zone transfer.
  113.     @type multi: bool
  114.     @ivar first: Is this message standalone, or the first of a multi
  115.     message sequence?  This variable is used when validating TSIG signatures
  116.     on messages which are part of a zone transfer.
  117.     @type first: bool
  118.     @ivar index: An index of rrsets in the message.  The index key is
  119.     (section, name, rdclass, rdtype, covers, deleting).  Indexing can be
  120.     disabled by setting the index to None.
  121.     @type index: dict
  122.     """
  123.     
  124.     def __init__(self, id = None):
  125.         if id is None:
  126.             self.id = random.randint(0, 65535)
  127.         else:
  128.             self.id = id
  129.         self.flags = 0
  130.         self.question = []
  131.         self.answer = []
  132.         self.authority = []
  133.         self.additional = []
  134.         self.edns = -1
  135.         self.ednsflags = 0
  136.         self.payload = 0
  137.         self.request_payload = 0
  138.         self.keyring = None
  139.         self.keyname = None
  140.         self.request_mac = ''
  141.         self.other_data = ''
  142.         self.tsig_error = 0
  143.         self.fudge = 300
  144.         self.original_id = self.id
  145.         self.mac = ''
  146.         self.xfr = False
  147.         self.origin = None
  148.         self.tsig_ctx = None
  149.         self.had_tsig = False
  150.         self.multi = False
  151.         self.first = True
  152.         self.index = { }
  153.  
  154.     
  155.     def __repr__(self):
  156.         return '<DNS message, ID ' + `self.id` + '>'
  157.  
  158.     
  159.     def __str__(self):
  160.         return self.to_text()
  161.  
  162.     
  163.     def to_text(self, origin = None, relativize = True, **kw):
  164.         '''Convert the message to text.
  165.  
  166.         The I{origin}, I{relativize}, and any other keyword
  167.         arguments are passed to the rrset to_wire() method.
  168.  
  169.         @rtype: string
  170.         '''
  171.         s = cStringIO.StringIO()
  172.         print >>s, 'id %d' % self.id
  173.         print >>s, 'opcode %s' % dns.opcode.to_text(dns.opcode.from_flags(self.flags))
  174.         rc = dns.rcode.from_flags(self.flags, self.ednsflags)
  175.         print >>s, 'rcode %s' % dns.rcode.to_text(rc)
  176.         print >>s, 'flags %s' % dns.flags.to_text(self.flags)
  177.         if self.edns >= 0:
  178.             print >>s, 'edns %s' % self.edns
  179.             if self.ednsflags != 0:
  180.                 print >>s, 'eflags %s' % dns.flags.edns_to_text(self.ednsflags)
  181.             
  182.             print >>s, 'payload', self.payload
  183.         
  184.         is_update = dns.opcode.is_update(self.flags)
  185.         if is_update:
  186.             print >>s, ';ZONE'
  187.         else:
  188.             print >>s, ';QUESTION'
  189.         for rrset in self.question:
  190.             print >>s, rrset.to_text(origin, relativize, **kw)
  191.         
  192.         if is_update:
  193.             print >>s, ';PREREQ'
  194.         else:
  195.             print >>s, ';ANSWER'
  196.         for rrset in self.answer:
  197.             print >>s, rrset.to_text(origin, relativize, **kw)
  198.         
  199.         if is_update:
  200.             print >>s, ';UPDATE'
  201.         else:
  202.             print >>s, ';AUTHORITY'
  203.         for rrset in self.authority:
  204.             print >>s, rrset.to_text(origin, relativize, **kw)
  205.         
  206.         print >>s, ';ADDITIONAL'
  207.         for rrset in self.additional:
  208.             print >>s, rrset.to_text(origin, relativize, **kw)
  209.         
  210.         return s.getvalue()[:-1]
  211.  
  212.     
  213.     def __eq__(self, other):
  214.         '''Two messages are equal if they have the same content in the
  215.         header, question, answer, and authority sections.
  216.         @rtype: bool'''
  217.         if not isinstance(other, Message):
  218.             return False
  219.         
  220.         if self.id != other.id:
  221.             return False
  222.         
  223.         if self.flags != other.flags:
  224.             return False
  225.         
  226.         for n in self.question:
  227.             if n not in other.question:
  228.                 return False
  229.                 continue
  230.         
  231.         for n in other.question:
  232.             if n not in self.question:
  233.                 return False
  234.                 continue
  235.         
  236.         for n in self.answer:
  237.             if n not in other.answer:
  238.                 return False
  239.                 continue
  240.         
  241.         for n in other.answer:
  242.             if n not in self.answer:
  243.                 return False
  244.                 continue
  245.         
  246.         for n in self.authority:
  247.             if n not in other.authority:
  248.                 return False
  249.                 continue
  250.         
  251.         for n in other.authority:
  252.             if n not in self.authority:
  253.                 return False
  254.                 continue
  255.         
  256.         return True
  257.  
  258.     
  259.     def __ne__(self, other):
  260.         '''Are two messages not equal?
  261.         @rtype: bool'''
  262.         return not self.__eq__(other)
  263.  
  264.     
  265.     def is_response(self, other):
  266.         '''Is other a response to self?
  267.         @rtype: bool'''
  268.         if other.flags & dns.flags.QR == 0 and self.id != other.id or dns.opcode.from_flags(self.flags) != dns.opcode.from_flags(other.flags):
  269.             return False
  270.         
  271.         if dns.rcode.from_flags(other.flags, other.ednsflags) != dns.rcode.NOERROR:
  272.             return True
  273.         
  274.         if dns.opcode.is_update(self.flags):
  275.             return True
  276.         
  277.         for n in self.question:
  278.             if n not in other.question:
  279.                 return False
  280.                 continue
  281.         
  282.         for n in other.question:
  283.             if n not in self.question:
  284.                 return False
  285.                 continue
  286.         
  287.         return True
  288.  
  289.     
  290.     def section_number(self, section):
  291.         if section is self.question:
  292.             return 0
  293.         elif section is self.answer:
  294.             return 1
  295.         elif section is self.authority:
  296.             return 2
  297.         elif section is self.additional:
  298.             return 3
  299.         else:
  300.             raise ValueError, 'unknown section'
  301.  
  302.     
  303.     def find_rrset(self, section, name, rdclass, rdtype, covers = dns.rdatatype.NONE, deleting = None, create = False, force_unique = False):
  304.         '''Find the RRset with the given attributes in the specified section.
  305.         
  306.         @param section: the section of the message to look in, e.g.
  307.         self.answer.
  308.         @type section: list of dns.rrset.RRset objects
  309.         @param name: the name of the RRset
  310.         @type name: dns.name.Name object
  311.         @param rdclass: the class of the RRset
  312.         @type rdclass: int
  313.         @param rdtype: the type of the RRset
  314.         @type rdtype: int
  315.         @param covers: the covers value of the RRset
  316.         @type covers: int
  317.         @param deleting: the deleting value of the RRset
  318.         @type deleting: int
  319.         @param create: If True, create the RRset if it is not found.
  320.         The created RRset is appended to I{section}.
  321.         @type create: bool
  322.         @param force_unique: If True and create is also True, create a
  323.         new RRset regardless of whether a matching RRset exists already.
  324.         @type force_unique: bool
  325.         @raises KeyError: the RRset was not found and create was False
  326.         @rtype: dns.rrset.RRset object'''
  327.         key = (self.section_number(section), name, rdclass, rdtype, covers, deleting)
  328.         if not force_unique:
  329.             if self.index is not None:
  330.                 rrset = self.index.get(key)
  331.                 if rrset is not None:
  332.                     return rrset
  333.                 
  334.             else:
  335.                 for rrset in section:
  336.                     if rrset.match(name, rdclass, rdtype, covers, deleting):
  337.                         return rrset
  338.                         continue
  339.                 
  340.         
  341.         if not create:
  342.             raise KeyError
  343.         
  344.         rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting)
  345.         section.append(rrset)
  346.         if self.index is not None:
  347.             self.index[key] = rrset
  348.         
  349.         return rrset
  350.  
  351.     
  352.     def get_rrset(self, section, name, rdclass, rdtype, covers = dns.rdatatype.NONE, deleting = None, create = False, force_unique = False):
  353.         '''Get the RRset with the given attributes in the specified section.
  354.  
  355.         If the RRset is not found, None is returned.
  356.         
  357.         @param section: the section of the message to look in, e.g.
  358.         self.answer.
  359.         @type section: list of dns.rrset.RRset objects
  360.         @param name: the name of the RRset
  361.         @type name: dns.name.Name object
  362.         @param rdclass: the class of the RRset
  363.         @type rdclass: int
  364.         @param rdtype: the type of the RRset
  365.         @type rdtype: int
  366.         @param covers: the covers value of the RRset
  367.         @type covers: int
  368.         @param deleting: the deleting value of the RRset
  369.         @type deleting: int
  370.         @param create: If True, create the RRset if it is not found.
  371.         The created RRset is appended to I{section}.
  372.         @type create: bool
  373.         @param force_unique: If True and create is also True, create a
  374.         new RRset regardless of whether a matching RRset exists already.
  375.         @type force_unique: bool
  376.         @rtype: dns.rrset.RRset object or None'''
  377.         
  378.         try:
  379.             rrset = self.find_rrset(section, name, rdclass, rdtype, covers, deleting, create, force_unique)
  380.         except KeyError:
  381.             rrset = None
  382.  
  383.         return rrset
  384.  
  385.     
  386.     def to_wire(self, origin = None, max_size = 0, **kw):
  387.         """Return a string containing the message in DNS compressed wire
  388.         format.
  389.  
  390.         Additional keyword arguments are passed to the rrset to_wire()
  391.         method.
  392.         
  393.         @param origin: The origin to be appended to any relative names.
  394.         @type origin: dns.name.Name object
  395.         @param max_size: The maximum size of the wire format output; default
  396.         is 0, which means 'the message's request payload, if nonzero, or
  397.         65536'.
  398.         @type max_size: int
  399.         @raises dns.exception.TooBig: max_size was exceeded
  400.         @rtype: string
  401.         """
  402.         if max_size == 0:
  403.             if self.request_payload != 0:
  404.                 max_size = self.request_payload
  405.             else:
  406.                 max_size = 65535
  407.         
  408.         if max_size < 512:
  409.             max_size = 512
  410.         elif max_size > 65535:
  411.             max_size = 65535
  412.         
  413.         r = dns.renderer.Renderer(self.id, self.flags, max_size, origin)
  414.         for rrset in self.question:
  415.             r.add_question(rrset.name, rrset.rdtype, rrset.rdclass)
  416.         
  417.         for rrset in self.answer:
  418.             r.add_rrset(dns.renderer.ANSWER, rrset, **kw)
  419.         
  420.         for rrset in self.authority:
  421.             r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw)
  422.         
  423.         if self.edns >= 0:
  424.             r.add_edns(self.edns, self.ednsflags, self.payload)
  425.         
  426.         for rrset in self.additional:
  427.             r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw)
  428.         
  429.         r.write_header()
  430.         if self.keyname is not None:
  431.             r.add_tsig(self.keyname, self.keyring[self.keyname], self.fudge, self.original_id, self.tsig_error, self.other_data, self.request_mac)
  432.             self.mac = r.mac
  433.         
  434.         return r.get_wire()
  435.  
  436.     
  437.     def use_tsig(self, keyring, keyname = None, fudge = 300, original_id = None, tsig_error = 0, other_data = ''):
  438.         """When sending, a TSIG signature using the specified keyring
  439.         and keyname should be added.
  440.         
  441.         @param keyring: The TSIG keyring to use; defaults to None.
  442.         @type keyring: dict
  443.         @param keyname: The name of the TSIG key to use; defaults to None.
  444.         The key must be defined in the keyring.  If a keyring is specified
  445.         but a keyname is not, then the key used will be the first key in the
  446.         keyring.  Note that the order of keys in a dictionary is not defined,
  447.         so applications should supply a keyname when a keyring is used, unless
  448.         they know the keyring contains only one key.
  449.         @type keyname: dns.name.Name or string
  450.         @param fudge: TSIG time fudge; default is 300 seconds.
  451.         @type fudge: int
  452.         @param original_id: TSIG original id; defaults to the message's id
  453.         @type original_id: int
  454.         @param tsig_error: TSIG error code; default is 0.
  455.         @type tsig_error: int
  456.         @param other_data: TSIG other data.
  457.         @type other_data: string
  458.         """
  459.         self.keyring = keyring
  460.         if keyname is None:
  461.             self.keyname = self.keyring.keys()[0]
  462.         elif isinstance(keyname, str):
  463.             keyname = dns.name.from_text(keyname)
  464.         
  465.         self.keyname = keyname
  466.         self.fudge = fudge
  467.         if original_id is None:
  468.             self.original_id = self.id
  469.         else:
  470.             self.original_id = original_id
  471.         self.tsig_error = tsig_error
  472.         self.other_data = other_data
  473.  
  474.     
  475.     def use_edns(self, edns, ednsflags, payload, request_payload = 0):
  476.         """Configure EDNS behavior.
  477.         @param edns: The EDNS level to use.  Specifying None or -1 means
  478.         'do not use EDNS'.
  479.         @type edns: int or None
  480.         @param ednsflags: EDNS flag values.
  481.         @type ednsflags: int
  482.         @param payload: The EDNS sender's payload field, which is the maximum
  483.         size of UDP datagram the sender can handle.
  484.         @type payload: int
  485.         @see: RFC 2671
  486.         """
  487.         if edns is None:
  488.             edns = -1
  489.         
  490.         self.edns = edns
  491.         self.ednsflags = ednsflags
  492.         self.payload = payload
  493.         self.request_payload = request_payload
  494.  
  495.     
  496.     def rcode(self):
  497.         '''Return the rcode.
  498.         @rtype: int
  499.         '''
  500.         return dns.rcode.from_flags(self.flags, self.ednsflags)
  501.  
  502.     
  503.     def set_rcode(self, rcode):
  504.         '''Set the rcode.
  505.         @param rcode: the rcode
  506.         @type rcode: int
  507.         '''
  508.         (value, evalue) = dns.rcode.to_flags(rcode)
  509.         self.flags &= 65520
  510.         self.flags |= value
  511.         self.ednsflags &= 0xFF000000L
  512.         self.ednsflags |= evalue
  513.  
  514.     
  515.     def opcode(self):
  516.         '''Return the opcode.
  517.         @rtype: int
  518.         '''
  519.         return dns.opcode.from_flags(self.flags)
  520.  
  521.     
  522.     def set_opcode(self, opcode):
  523.         '''Set the opcode.
  524.         @param opcode: the opcode
  525.         @type opcode: int
  526.         '''
  527.         self.flags &= 34815
  528.         self.flags |= dns.opcode.to_flags(opcode)
  529.  
  530.  
  531.  
  532. class _WireReader(object):
  533.     '''Wire format reader.
  534.  
  535.     @ivar wire: the wire-format message.
  536.     @type wire: string
  537.     @ivar message: The message object being built
  538.     @type message: dns.message.Message object
  539.     @ivar current: When building a message object from wire format, this
  540.     variable contains the offset from the beginning of wire of the next octet
  541.     to be read.
  542.     @type current: int
  543.     @ivar updating: Is the message a dynamic update?
  544.     @type updating: bool
  545.     @ivar zone_rdclass: The class of the zone in messages which are
  546.     DNS dynamic updates.
  547.     @type zone_rdclass: int
  548.     '''
  549.     
  550.     def __init__(self, wire, message, question_only = False):
  551.         self.wire = wire
  552.         self.message = message
  553.         self.current = 0
  554.         self.updating = False
  555.         self.zone_rdclass = dns.rdataclass.IN
  556.         self.question_only = question_only
  557.  
  558.     
  559.     def _get_question(self, qcount):
  560.         '''Read the next I{qcount} records from the wire data and add them to
  561.         the question section.
  562.         @param qcount: the number of questions in the message
  563.         @type qcount: int'''
  564.         if self.updating and qcount > 1:
  565.             raise dns.exception.FormError
  566.         
  567.         for i in xrange(0, qcount):
  568.             (qname, used) = dns.name.from_wire(self.wire, self.current)
  569.             if self.message.origin is not None:
  570.                 qname = qname.relativize(self.message.origin)
  571.             
  572.             self.current = self.current + used
  573.             (rdtype, rdclass) = struct.unpack('!HH', self.wire[self.current:self.current + 4])
  574.             self.current = self.current + 4
  575.             self.message.find_rrset(self.message.question, qname, rdclass, rdtype, create = True, force_unique = True)
  576.             if self.updating:
  577.                 self.zone_rdclass = rdclass
  578.                 continue
  579.         
  580.  
  581.     
  582.     def _get_section(self, section, count):
  583.         '''Read the next I{count} records from the wire data and add them to
  584.         the specified section.
  585.         @param section: the section of the message to which to add records
  586.         @type section: list of dns.rrset.RRset objects
  587.         @param count: the number of records to read
  588.         @type count: int'''
  589.         if self.updating:
  590.             force_unique = True
  591.         else:
  592.             force_unique = False
  593.         seen_opt = False
  594.         for i in xrange(0, count):
  595.             rr_start = self.current
  596.             (name, used) = dns.name.from_wire(self.wire, self.current)
  597.             if self.message.origin is not None:
  598.                 name = name.relativize(self.message.origin)
  599.             
  600.             self.current = self.current + used
  601.             (rdtype, rdclass, ttl, rdlen) = struct.unpack('!HHIH', self.wire[self.current:self.current + 10])
  602.             self.current = self.current + 10
  603.             if rdtype == dns.rdatatype.OPT:
  604.                 if section is not self.message.additional or seen_opt:
  605.                     raise BadEDNS
  606.                 
  607.                 self.message.payload = rdclass
  608.                 self.message.ednsflags = ttl
  609.                 self.message.edns = (ttl & 16711680) >> 16
  610.                 seen_opt = True
  611.             elif rdtype == dns.rdatatype.TSIG:
  612.                 if not section is self.message.additional and i == count - 1:
  613.                     raise BadTSIG
  614.                 
  615.                 if self.message.keyring is None:
  616.                     raise UnknownTSIGKey, 'got signed message without keyring'
  617.                 
  618.                 secret = self.message.keyring.get(name)
  619.                 if secret is None:
  620.                     raise UnknownTSIGKey, "key '%s' unknown" % name
  621.                 
  622.                 self.message.tsig_ctx = dns.tsig.validate(self.wire, name, secret, int(time.time()), self.message.request_mac, rr_start, self.current, rdlen, self.message.tsig_ctx, self.message.multi, self.message.first)
  623.                 self.message.had_tsig = True
  624.             elif ttl < 0:
  625.                 ttl = 0
  626.             
  627.             if self.updating:
  628.                 if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
  629.                     deleting = rdclass
  630.                     rdclass = self.zone_rdclass
  631.                 else:
  632.                     deleting = None
  633.             if deleting == dns.rdataclass.ANY:
  634.                 covers = dns.rdatatype.NONE
  635.                 rd = None
  636.             else:
  637.                 rd = dns.rdata.from_wire(rdclass, rdtype, self.wire, self.current, rdlen, self.message.origin)
  638.                 covers = rd.covers()
  639.             if self.message.xfr and rdtype == dns.rdatatype.SOA:
  640.                 force_unique = True
  641.             
  642.             rrset = self.message.find_rrset(section, name, rdclass, rdtype, covers, deleting, True, force_unique)
  643.             if rd is not None:
  644.                 rrset.add(rd, ttl)
  645.             
  646.             self.current = self.current + rdlen
  647.         
  648.  
  649.     
  650.     def read(self):
  651.         '''Read a wire format DNS message and build a dns.message.Message
  652.         object.'''
  653.         l = len(self.wire)
  654.         if l < 12:
  655.             raise ShortHeader
  656.         
  657.         (self.message.id, self.message.flags, qcount, ancount, aucount, adcount) = struct.unpack('!HHHHHH', self.wire[:12])
  658.         self.current = 12
  659.         if dns.opcode.is_update(self.message.flags):
  660.             self.updating = True
  661.         
  662.         self._get_question(qcount)
  663.         if self.question_only:
  664.             return None
  665.         
  666.         self._get_section(self.message.answer, ancount)
  667.         self._get_section(self.message.authority, aucount)
  668.         self._get_section(self.message.additional, adcount)
  669.         if self.current != l:
  670.             raise TrailingJunk
  671.         
  672.         if self.message.multi and self.message.tsig_ctx and not (self.message.had_tsig):
  673.             self.message.tsig_ctx.update(self.wire)
  674.         
  675.  
  676.  
  677.  
  678. def from_wire(wire, keyring = None, request_mac = '', xfr = False, origin = None, tsig_ctx = None, multi = False, first = True, question_only = False):
  679.     '''Convert a DNS wire format message into a message
  680.     object.
  681.  
  682.     @param keyring: The keyring to use if the message is signed.
  683.     @type keyring: dict
  684.     @param request_mac: If the message is a response to a TSIG-signed request,
  685.     I{request_mac} should be set to the MAC of that request.
  686.     @type request_mac: string
  687.     @param xfr: Is this message part of a zone transfer?
  688.     @type xfr: bool
  689.     @param origin: If the message is part of a zone transfer, I{origin}
  690.     should be the origin name of the zone.
  691.     @type origin: dns.name.Name object
  692.     @param tsig_ctx: The ongoing TSIG context, used when validating zone
  693.     transfers.
  694.     @type tsig_ctx: hmac.HMAC object
  695.     @param multi: Is this message part of a multiple message sequence?
  696.     @type multi: bool
  697.     @param first: Is this message standalone, or the first of a multi
  698.     message sequence?
  699.     @type first: bool
  700.     @param question_only: Read only up to the end of the question section?
  701.     @type question_only: bool
  702.     @raises ShortHeader: The message is less than 12 octets long.
  703.     @raises TrailingJunk: There were octets in the message past the end
  704.     of the proper DNS message.
  705.     @raises BadEDNS: An OPT record was in the wrong section, or occurred more
  706.     than once.
  707.     @raises BadTSIG: A TSIG record was not the last record of the additional
  708.     data section.
  709.     @rtype: dns.message.Message object'''
  710.     m = Message(id = 0)
  711.     m.keyring = keyring
  712.     m.request_mac = request_mac
  713.     m.xfr = xfr
  714.     m.origin = origin
  715.     m.tsig_ctx = tsig_ctx
  716.     m.multi = multi
  717.     m.first = first
  718.     reader = _WireReader(wire, m, question_only)
  719.     reader.read()
  720.     return m
  721.  
  722.  
  723. class _TextReader(object):
  724.     '''Text format reader.
  725.     
  726.     @ivar tok: the tokenizer
  727.     @type tok: dns.tokenizer.Tokenizer object
  728.     @ivar message: The message object being built
  729.     @type message: dns.message.Message object
  730.     @ivar updating: Is the message a dynamic update?
  731.     @type updating: bool
  732.     @ivar zone_rdclass: The class of the zone in messages which are
  733.     DNS dynamic updates.
  734.     @type zone_rdclass: int
  735.     @ivar last_name: The most recently read name when building a message object
  736.     from text format.
  737.     @type last_name: dns.name.Name object
  738.     '''
  739.     
  740.     def __init__(self, text, message):
  741.         self.message = message
  742.         self.tok = dns.tokenizer.Tokenizer(text)
  743.         self.last_name = None
  744.         self.zone_rdclass = dns.rdataclass.IN
  745.         self.updating = False
  746.  
  747.     
  748.     def _header_line(self, section):
  749.         '''Process one line from the text format header section.'''
  750.         (ttype, what) = self.tok.get()
  751.         if what == 'id':
  752.             self.message.id = self.tok.get_int()
  753.         elif what == 'flags':
  754.             while True:
  755.                 token = self.tok.get()
  756.                 if token[0] != dns.tokenizer.IDENTIFIER:
  757.                     self.tok.unget(token)
  758.                     break
  759.                 
  760.                 self.message.flags = self.message.flags | dns.flags.from_text(token[1])
  761.             if dns.opcode.is_update(self.message.flags):
  762.                 self.updating = True
  763.             
  764.         elif what == 'edns':
  765.             self.message.edns = self.tok.get_int()
  766.             self.message.ednsflags = self.message.ednsflags | self.message.edns << 16
  767.         elif what == 'eflags':
  768.             if self.message.edns < 0:
  769.                 self.message.edns = 0
  770.             
  771.             while True:
  772.                 token = self.tok.get()
  773.                 if token[0] != dns.tokenizer.IDENTIFIER:
  774.                     self.tok.unget(token)
  775.                     break
  776.                 
  777.                 self.message.ednsflags = self.message.ednsflags | dns.flags.edns_from_text(token[1])
  778.         elif what == 'payload':
  779.             self.message.payload = self.tok.get_int()
  780.             if self.message.edns < 0:
  781.                 self.message.edns = 0
  782.             
  783.         elif what == 'opcode':
  784.             text = self.tok.get_string()
  785.             self.message.flags = self.message.flags | dns.opcode.to_flags(dns.opcode.from_text(text))
  786.         elif what == 'rcode':
  787.             text = self.tok.get_string()
  788.             self.message.set_rcode(dns.rcode.from_text(text))
  789.         else:
  790.             raise UnknownHeaderField
  791.         self.tok.get_eol()
  792.  
  793.     
  794.     def _question_line(self, section):
  795.         '''Process one line from the text format question section.'''
  796.         token = self.tok.get(want_leading = True)
  797.         if token[0] != dns.tokenizer.WHITESPACE:
  798.             self.last_name = dns.name.from_text(token[1], None)
  799.         
  800.         name = self.last_name
  801.         token = self.tok.get()
  802.         if token[0] != dns.tokenizer.IDENTIFIER:
  803.             raise dns.exception.SyntaxError
  804.         
  805.         
  806.         try:
  807.             rdclass = dns.rdataclass.from_text(token[1])
  808.             token = self.tok.get()
  809.             if token[0] != dns.tokenizer.IDENTIFIER:
  810.                 raise dns.exception.SyntaxError
  811.         except dns.exception.SyntaxError:
  812.             raise dns.exception.SyntaxError
  813.         except:
  814.             rdclass = dns.rdataclass.IN
  815.  
  816.         rdtype = dns.rdatatype.from_text(token[1])
  817.         self.message.find_rrset(self.message.question, name, rdclass, rdtype, create = True, force_unique = True)
  818.         if self.updating:
  819.             self.zone_rdclass = rdclass
  820.         
  821.         self.tok.get_eol()
  822.  
  823.     
  824.     def _rr_line(self, section):
  825.         '''Process one line from the text format answer, authority, or
  826.         additional data sections.
  827.         '''
  828.         deleting = None
  829.         token = self.tok.get(want_leading = True)
  830.         if token[0] != dns.tokenizer.WHITESPACE:
  831.             self.last_name = dns.name.from_text(token[1], None)
  832.         
  833.         name = self.last_name
  834.         token = self.tok.get()
  835.         if token[0] != dns.tokenizer.IDENTIFIER:
  836.             raise dns.exception.SyntaxError
  837.         
  838.         
  839.         try:
  840.             ttl = int(token[1], 0)
  841.             token = self.tok.get()
  842.             if token[0] != dns.tokenizer.IDENTIFIER:
  843.                 raise dns.exception.SyntaxError
  844.         except dns.exception.SyntaxError:
  845.             raise dns.exception.SyntaxError
  846.         except:
  847.             ttl = 0
  848.  
  849.         
  850.         try:
  851.             rdclass = dns.rdataclass.from_text(token[1])
  852.             token = self.tok.get()
  853.             if token[0] != dns.tokenizer.IDENTIFIER:
  854.                 raise dns.exception.SyntaxError
  855.             
  856.             if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
  857.                 deleting = rdclass
  858.                 rdclass = self.zone_rdclass
  859.         except dns.exception.SyntaxError:
  860.             raise dns.exception.SyntaxError
  861.         except:
  862.             rdclass = dns.rdataclass.IN
  863.  
  864.         rdtype = dns.rdatatype.from_text(token[1])
  865.         token = self.tok.get()
  866.         if token[0] != dns.tokenizer.EOL and token[0] != dns.tokenizer.EOF:
  867.             self.tok.unget(token)
  868.             rd = dns.rdata.from_text(rdclass, rdtype, self.tok, None)
  869.             covers = rd.covers()
  870.         else:
  871.             rd = None
  872.             covers = dns.rdatatype.NONE
  873.         rrset = self.message.find_rrset(section, name, rdclass, rdtype, covers, deleting, True, self.updating)
  874.         if rd is not None:
  875.             rrset.add(rd, ttl)
  876.         
  877.  
  878.     
  879.     def read(self):
  880.         '''Read a text format DNS message and build a dns.message.Message
  881.         object.'''
  882.         line_method = self._header_line
  883.         section = None
  884.         while None:
  885.             token = self.tok.get(True, True)
  886.             if token[0] == dns.tokenizer.EOL or token[0] == dns.tokenizer.EOF:
  887.                 break
  888.             
  889.             if token[0] == dns.tokenizer.COMMENT:
  890.                 u = token[1].upper()
  891.                 if u == 'HEADER':
  892.                     line_method = self._header_line
  893.                 elif u == 'QUESTION' or u == 'ZONE':
  894.                     line_method = self._question_line
  895.                     section = self.message.question
  896.                 elif u == 'ANSWER' or u == 'PREREQ':
  897.                     line_method = self._rr_line
  898.                     section = self.message.answer
  899.                 elif u == 'AUTHORITY' or u == 'UPDATE':
  900.                     line_method = self._rr_line
  901.                     section = self.message.authority
  902.                 elif u == 'ADDITIONAL':
  903.                     line_method = self._rr_line
  904.                     section = self.message.additional
  905.                 
  906.                 self.tok.get_eol()
  907.                 continue
  908.             
  909.             line_method(section)
  910.  
  911.  
  912.  
  913. def from_text(text):
  914.     '''Convert the text format message into a message object.
  915.  
  916.     @param text: The text format message.
  917.     @type text: string
  918.     @raises UnknownHeaderField:
  919.     @raises dns.exception.SyntaxError:
  920.     @rtype: dns.message.Message object'''
  921.     m = Message()
  922.     reader = _TextReader(text, m)
  923.     reader.read()
  924.     return m
  925.  
  926.  
  927. def from_file(f):
  928.     '''Read the next text format message from the specified file.
  929.  
  930.     @param f: file or string.  If I{f} is a string, it is treated
  931.     as the name of a file to open.
  932.     @raises UnknownHeaderField:
  933.     @raises dns.exception.SyntaxError:
  934.     @rtype: dns.message.Message object'''
  935.     if sys.hexversion >= 33751040:
  936.         str_type = basestring
  937.         opts = 'rU'
  938.     else:
  939.         str_type = str
  940.         opts = 'r'
  941.     if isinstance(f, str_type):
  942.         f = file(f, opts)
  943.         want_close = True
  944.     else:
  945.         want_close = False
  946.     
  947.     try:
  948.         m = from_text(f)
  949.     finally:
  950.         if want_close:
  951.             f.close()
  952.         
  953.  
  954.     return m
  955.  
  956.  
  957. def make_query(qname, rdtype, rdclass = dns.rdataclass.IN):
  958.     '''Make a query message.
  959.  
  960.     The query name, type, and class may all be specified either
  961.     as objects of the appropriate type, or as strings.
  962.  
  963.     The query will have a randomly choosen query id, and its DNS flags
  964.     will be set to dns.flags.RD.
  965.     
  966.     @param qname: The query name.
  967.     @type qname: dns.name.Name object or string
  968.     @param rdtype: The desired rdata type.
  969.     @type rdtype: int
  970.     @param rdclass: The desired rdata class; the default is class IN.
  971.     @type rdclass: int
  972.     @rtype: dns.message.Message object'''
  973.     if isinstance(qname, str):
  974.         qname = dns.name.from_text(qname)
  975.     
  976.     if isinstance(rdtype, str):
  977.         rdtype = dns.rdatatype.from_text(rdtype)
  978.     
  979.     if isinstance(rdclass, str):
  980.         rdclass = dns.rdataclass.from_text(rdclass)
  981.     
  982.     m = Message()
  983.     m.flags |= dns.flags.RD
  984.     m.find_rrset(m.question, qname, rdclass, rdtype, create = True, force_unique = True)
  985.     return m
  986.  
  987.  
  988. def make_response(query, recursion_available = False, our_payload = 8192):
  989.     """Make a message which is a response for the specified query.
  990.     The message returned is really a response skeleton; it has all
  991.     of the infrastructure required of a response, but none of the
  992.     content.
  993.  
  994.     The response's question section is a shallow copy of the query's
  995.     question section, so the query's question RRsets should not be
  996.     changed.
  997.     
  998.     @param query: the query to respond to
  999.     @type query: dns.message.Message object
  1000.     @param recursion_available: should RA be set in the response?
  1001.     @type recursion_available: bool
  1002.     @param our_payload: payload size to advertise in EDNS responses; default
  1003.     is 8192.
  1004.     @type our_payload: int
  1005.     @rtype: dns.message.Message object"""
  1006.     if query.flags & dns.flags.QR:
  1007.         raise dns.exception.FormError, 'specified query message is not a query'
  1008.     
  1009.     response = dns.message.Message(query.id)
  1010.     response.flags = dns.flags.QR | query.flags & dns.flags.RD
  1011.     if recursion_available:
  1012.         response.flags |= dns.flags.RA
  1013.     
  1014.     response.set_opcode(query.opcode())
  1015.     response.question = list(query.question)
  1016.     if query.edns >= 0:
  1017.         response.use_edns(0, 0, our_payload, query.payload)
  1018.     
  1019.     if query.keyname is not None:
  1020.         response.keyname = query.keyname
  1021.         response.keyring = query.keyring
  1022.         response.request_mac = query.mac
  1023.     
  1024.     return response
  1025.  
  1026.